前言
当我们接到一段 JavaScript
代码的时候,传递给 JavaScript
引擎要求其执行。在遇到事件的时候,会继续将其中的一些事件代码交给 JavaScript
引擎执行,当然也会提供一些 API
给 JavaScript
引擎,这样的 API
允许引擎在特定时机执行某段代码。
我们可以理解为 JavaScript
引擎存在于内存中,它一直在运转,在等待着我们将 JavaScript
片段代码或者事件交给它进行执行处理,但是页面一般同时会有很多代码需要执行,那么就需要指定一个顺序。
宏任务与微任务
我们把宿主发起的任务称为宏观任务,把 JavaScript 引擎发起的任务称为微观任务。
JavaScript
引擎一直在等待着宿主分配宏观任务,这个等待分配,然后执行代码的过程,称之为 evenLoop
1 | // 伪代码表述简单的evenLoop |
当然在等待以及执行的时候,还有其他的过程,像是检查任务队列,判断任务是否达到执行时机,判断同步的上一个任务是不是执行完毕等等。
而宏任务的队列就是事件循环。而 JavaScript
在执行宏观任务的时候,如果这个任务是异步的, JavaScript
要保证异步代码在一个宏观任务中执行,那么宏观任务中就有微任务队列
有了宏任务以及微任务概念之后,就可以理解,引擎产生的任务会在任务尾部添加微任务。而宿主产生的任务会直接扔在宏任务内
promise
1 | var promise = new Promise(function(resolve, reject){ |
上面的运行结果之所是 acb
是因为 new
一个构造函数,会执行构造函数内的函数,然后虽然 console.log("c")
在 .then()
后面,但是因为 resolve()
是异步的。所以会先打印 c
setTimeout与promise混合使用
1 | var promise = new Promise(function(resolve, reject){ |
上面的运行结果是 abcd
,代码从上向下扫射运行。先是运行 new
关键字的构造函数里的代码,打印 a
,遇到 resolve()
运行 .then
的代码,但是由于这个是异步的,所以就会继续往下运行代码, setTimeout
的代码会放在任务队列里。而事件队列里的代码会等主线程的代码运行完之后,才会检查事件队列。所以下一步打印的是 b
,然后由于 promise
是 JavaScript
引擎产生的微任务,而 setTimeout
是浏览器产生的宏任务,所有先打印 c
再打印 d
微任务是永远优先于宏任务的
1 | setTimeout(()=>console.log("d"), 0) |
上面的代码运行结果为 c1/c2/d
由上面的两个示例可以得出
- 先确定有多少个宏任务、
- 然后确定每个宏任务里有多少个微任务
- 根据调用的循序,确定每个微任务的执行顺序
- 然后将整个的环境执行顺序确定下来
评论加载中